package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"time"

	. "gitee.com/cloudapex/Ghost/utd"
)

// 生成一条全大写的消息类型string 驼峰变为下划线
func MakeOneMsgType(msgName string) string {
	msgName = strings.TrimPrefix(msgName, "Msg")
	name := []rune(msgName)
	fields := make([]string, 0, 10)
	begin := 0
	for index, char := range name {
		if char < 97 && index-begin > 0 {
			if char == '2' || name[index-1] == '2' {
				continue
			}
			fields = append(fields, string(name[begin:index]))
			begin = index
		}
	}
	fields = append(fields, string(name[begin:len(name)]))
	result := strings.Join(fields, "_")
	return strings.ToUpper(MESSAGE_ID_PREFIX + result)
}
func ModifyMsgEnumName(old string) string {
	name := []rune(old)
	result := make([]rune, 0)
	for _, char := range name {
		if char < 97 && char >= 65 { // 只取大写
			result = append(result, char)
		}
	}
	return "E" + string(result)
}
func ModifyMsgEnumName2(old string) string {
	return "E" + strings.TrimPrefix(old, "Msg")
}
func MakeMsgTypeAndName() error {
	var dir []os.FileInfo
	var err error

	if dir, err = ioutil.ReadDir("./"); err != nil {
		return err
	}

	for _, fi := range dir {
		if !fi.IsDir() { // 只要目录
			continue
		}
		// 目录必须是 Message or Msg 打头的目录名称
		if !(strings.HasPrefix(fi.Name(), "Message") || strings.HasPrefix(fi.Name(), "Msg")) {
			continue
		}
		if !IsIncludeDir(fi.Name()) {
			continue
		}
		// 先读取协议类型文件
		theDirName := fmt.Sprintf("./%s/", fi.Name())
		if err = readMsgTypeFile(theDirName); err != nil {
			return err
		}
		// 再读取协议名称文件
		if err = readMsgProtoFile(theDirName); err != nil {
			return err
		}
		// 修整协议类型文件
		if err = modyMsgTypeFile(theDirName); err != nil {
			return err
		}
	}
	if len(MsgStructNames) == 0 { // 没有具体协议结构
		return errors.New("没有发现任何协议结构")
	}

	return nil
}
func readMsgTypeFile(dir string) error {
	MsgTypeNames = make(map[string]int, 100)
	MsgTypeLines = make([]string, 0, 100)

	if !File.Exist(dir + MESSAGE_TYPE_FILE) {
		return nil
	}
	f, err := os.Open(dir + MESSAGE_TYPE_FILE)
	if err != nil {
		return err
	}
	defer f.Close()

	// 按行读取
	buf := bufio.NewReader(f)
	for {
		line, err := buf.ReadString('\n')
		line = strings.TrimSpace(line) // 去掉前后的空白

		// 如果此条消息类型被注释了 就丢弃这个消息类型字符串了
		if strings.HasPrefix(line, MESSAGE_ID_PREFIX) { // begin with MT_
			msgType := line
			// 如果此条消息是后注释 则只吧这条消息类型字符串解析出来.. 整条语句存储起来 不会丢弃
			if index := strings.Index(msgType, "//"); index != -1 {
				msgType = line[0:index]
				msgType = strings.TrimSpace(msgType)
			}
			msgType = strings.Trim(msgType, ", ")           // 其他语言可能有,号
			MsgTypeNames[msgType] = 1                       // 添加协议类型到map
			MsgTypeLines = append(MsgTypeLines, line)       // 此行有效,保存当前行信息到map
			MsgTypeLinesAll = append(MsgTypeLinesAll, line) // 汇总
		}

		if err != nil {
			if err == io.EOF {
				return nil
			}
			return err
		}
	}
}

func readMsgProtoFile(dir string) error {
	MsgStructNames = make([]string, 0, 100)
	rdir, err := ioutil.ReadDir(dir)
	if err != nil {
		return err
	}
	suffix := strings.ToUpper(FILE_PROTO_SUFFIX) //忽略后缀大小写匹配
	for _, fi := range rdir {
		if fi.IsDir() { // 忽略目录
			continue
		}
		if strings.HasSuffix(strings.ToUpper(fi.Name()), suffix) { // 匹配文件.proto
			// 包含 '-'负号则认为是注释
			if strings.Contains(fi.Name(), "-") {
				continue
			}
			// 文件名必须以 Msg 为前缀
			if !strings.HasPrefix(fi.Name(), MESSAGE_STRUCT_PREFIX) {
				continue
			}

			f, err := os.Open(dir + fi.Name())
			if err != nil {
				return err
			}
			// 按行读取
			buf := bufio.NewReader(f)
			msgs := make([]string, 0, 2)
			for {
				line, err2 := buf.ReadString('\n')
				if err2 != nil {
					if err2 == io.EOF {
						break
					}
					f.Close()
					return err2
				}
				line = strings.TrimSpace(line)           // 去掉前后的空白
				if !strings.HasPrefix(line, "message") { // message Msgxxxx
					continue
				}

				fields := strings.Split(line, " ")
				if len(fields) < 2 || !strings.HasPrefix(fields[1], MESSAGE_STRUCT_PREFIX) { // 以 Msg 为前缀
					continue
				}

				msgs = append(msgs, fields[1])
			}
			f.Close()
			for _, msgName := range msgs {
				MsgStructNames = append(MsgStructNames, msgName)
				MsgStructNamesAll = append(MsgStructNamesAll, msgName)
			}
		}
	}
	for _, value := range MsgStructNames {
		// 得到全大写的消息类型string
		msgType := MakeOneMsgType(value)
		// 检测在VecMsgTypes里面有没有已经存在的
		if _, ok := MsgTypeNames[msgType]; !ok {
			// 没有则添加到全局的vec里面待会等待写入文件
			if len(MsgTypeLines) == 0 {
				headMsgType := MakeOneMsgType(strings.Split(dir, "/")[1]) + fmt.Sprintf("_BEGIN = %d, 	//此行不可删除", len(MsgTypeLinesAll))
				MsgTypeLines = append(MsgTypeLines, headMsgType)
				MsgTypeLinesAll = append(MsgTypeLinesAll, headMsgType)
			}
			MsgTypeLines = append(MsgTypeLines, msgType)
			MsgTypeLinesAll = append(MsgTypeLinesAll, msgType)
		}
	}
	return nil
}

// 修正现有的Message.Type文件
func modyMsgTypeFile(dir string) error {
	os.Remove(dir + MESSAGE_TYPE_FILE)
	file, err := os.OpenFile(dir+MESSAGE_TYPE_FILE, os.O_CREATE, 0666)
	if err != nil {
		return err
	}
	str := `/*******************************************************************************
Copyright:cloud
Author:cloudapex@126.com
Version:2.0
Date:%s
Description: 协议号定义
	# 此文件可手动维护也可由GPack生成
	# 此文件也参与编译
	# 通常只需要关注首行定义Begin值和协议号的顺序
	# 格式为:[全大写的消息名称, // 注释...] 的形式    
*******************************************************************************/
enum EMsgId{
`
	str = fmt.Sprintf(str, time.Now().Format("2006-01-02"))
	file.WriteString(str)
	for _, value := range MsgTypeLines {
		value = strings.Replace(value, ",", "", 1)
		file.WriteString("	" + value + "\n")
	}
	file.WriteString("}")
	file.Close()
	return nil
}

//把所有散着的协议结构组合到 此文件中./Protocols.proto
func MergeAllProtoFiles() error {
	var dir []os.FileInfo
	var file *os.File
	var err error

	// 先把当前目录文件合并
	if dir, err = ioutil.ReadDir("./"); err != nil {
		return err
	}
	for _, fi := range dir {
		if fi.IsDir() { // 过滤目录
			continue
		}
		if strings.Contains(fi.Name(), FILE_NAME_PROTO) { // 过滤 Protocols.proto
			continue
		}
		// 检测前缀
		if !(strings.HasPrefix(fi.Name(), "Message") || strings.HasPrefix(fi.Name(), "Msg")) {
			continue
		}

		// 检测后缀
		if !strings.HasSuffix(fi.Name(), "."+FILE_PROTO_SUFFIX) {
			continue
		}

		if file, err = os.Open("./" + fi.Name()); err != nil {
			return err
		}
		if err = appendToMerageFile(bufio.NewReader(file)); err != nil {
			return err
		}
		file.Close()
	}
	// 在遍历子目录进行合并
	for _, fi := range dir {
		if !fi.IsDir() {
			continue
		}
		if !(strings.HasPrefix(fi.Name(), "Message") || strings.HasPrefix(fi.Name(), "Msg")) {
			continue
		}
		if !IsIncludeDir(fi.Name()) {
			continue
		}
		if err = merageDirProtoFile("./" + fi.Name() + "/"); err != nil {
			return err
		}
	}

	// 不要忘记了关闭
	if MergedTempProtoFile != nil {
		MergedTempProtoFile.Close()
	}
	return nil
}
func merageDirProtoFile(fileName string) error {
	var dir []os.FileInfo
	var file *os.File
	var err error

	if dir, err = ioutil.ReadDir(fileName); err != nil {
		return err
	}
	for _, fi := range dir {
		if fi.IsDir() {
			merageDirProtoFile(fileName + fi.Name() + "/")
			continue
		}
		if strings.Contains(fi.Name(), MESSAGE_TYPE_FILE) {
			continue
		}
		if !strings.HasSuffix(fi.Name(), "."+FILE_PROTO_SUFFIX) {
			continue
		}
		if file, err = os.Open(fileName + fi.Name()); err != nil {
			return err
		}
		if err = appendToMerageFile(bufio.NewReader(file)); err != nil {
			return err
		}
		file.Close()
	}
	return nil
}
func appendToMerageFile(r *bufio.Reader) error {
	var err error
	isFirst := MergedTempProtoFile == nil
	if isFirst { //首次清理+创建
		tempFileName := fmt.Sprintf("./%s.%s", FILE_NAME_PROTO, FILE_PROTO_SUFFIX)
		os.Remove(tempFileName)
		MergedTempProtoFile, err = os.OpenFile(tempFileName, os.O_CREATE, 0666)
		if err != nil {
			return err
		}
	}
	var isPass bool

	for {
		line, err := r.ReadString('\n')
		if err != nil {
			if err == io.EOF {
				line += "\n"
				MergedTempProtoFile.WriteString(line)
				return nil
			}
			return err
		}
		if isFirst || isPass {
			MergedTempProtoFile.WriteString(line)
		} else if strings.HasPrefix(line, PROTO_PACKAGE_PREFIX) { // 只写有效的 [package xxxx; 后面的都是有效的]
			isPass = true
		}
	}
}
